﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using COFE.Core;
using COFE.Core.Lister;
using COFE.Core.Actions;
using System.IO;
using COFE.Core.Utils;
using COFE.Core.Entry;

namespace COFE.IO.SZ
{
    public class SevenZipActionProvider : ISecondaryPropertyProvider<IEntryList>
    {
        #region Constructor

        static SevenZipActionProvider()
        {
            COFEShared.PropertyDictionary.RegisterProperties<SevenZipActions>();

        }


        #endregion

        #region Methods

        public object[] GetSupportedProperty(IEntryList sourceObj)
        {
            if (sourceObj is ICustomEntryList)
                if (sourceObj.Entries.Count() == 1 &&
                    sourceObj.Entries.First() is IDirectoryInfo)
                {
                    var sourceDir = sourceObj.Entries.First() as IDirectoryInfo;
                    if (sourceDir.InternalProperties.DirectoryLister is SevenZipDirectoryLister)
                        return new object[] { InternalActions.Transfer };
                }

            return new object[] { SevenZipActions.SevenZip, SevenZipActions.Zip };
        }

        public bool IsPropertyReadOnly(IEntryList sourceObj, object property)
        {
            return true;
        }

        public PropertyPair GetProperty(IEntryList sourceObj, object property)
        {
            if (GetSupportedProperty(sourceObj).Contains(property))
            {
                switch ((int)property)
                {
                    case (int)InternalActions.Transfer:
                        return new PropertyPair(property, (Func<IEntryList, string, IWorkProgress, IEntryList>)Transfer);

                    case (int)SevenZipActions.SevenZip:
                        return new PropertyPair(property, (Func<IEntryList, string, IWorkProgress, IEntryList>)CompressSevenZip);

                    case (int)SevenZipActions.Zip:
                        return new PropertyPair(property, (Func<IEntryList, string, IWorkProgress, IEntryList>)CompressZip);
                }
            }

            throw new NotSupportedException(property != null ? property.ToString() : "");
        }

        public void SetProperty(IEntryList sourceObj, object property, object value)
        {
            throw new NotImplementedException();
        }

        IEntryList Transfer(IEntryList input, string param, IWorkProgress progress)
        {
            var stringDic = StringUtils.ParamStringToDic(param);
            if (!stringDic.ContainsKey("dest"))
                throw new ArgumentException("dest not specified.");
            var destDir = COFEShared.CurrentUserProfile.TryParsePath(stringDic["dest"]) as IInternalDirectoryInfoExA;
            if (destDir == null)
                throw new ArgumentException("dest not a directory or not found.");
            SZEntryInfo destDirInfo = destDir.InternalProperties.GetProperty(InternalProperties.EmbeddedEntry).Value as SZEntryInfo;
            if (destDirInfo == null)
                throw new Exception();

            Dictionary<string, Stream> addDic = new Dictionary<string, Stream>();
            ulong totalSize = 0;

            Action<string, IFileSystemInfo> register = null;
            register = (path, entry) =>
                {
                    if (entry.InternalProperties.GetProperty(DirectoryInfoProperties.IsFileBasedFS).ValueAsBoolean)
                        entry = entry.InternalProperties.GetProperty(DirectoryInfoProperties.FileBasedFSFile).Value as IFileSystemInfo;

                    if (entry is IFileInfo)
                    {
                        var fileEntry = entry as IFileInfo;
                        addDic.Add(COFEPath.Combine(path, entry.Name), fileEntry.OpenRead());
                        totalSize += (ulong)fileEntry.Length;
                    }                        
                    else
                    {
                        var dirEntry = entry as IDirectoryInfo;
                        var dirEntryPath = COFEPath.Combine(path, dirEntry.Name);
                        foreach (var e in dirEntry.EnumerateFileSystemInfos())
                        {
                            progress.CheckPause();
                            register(dirEntryPath, e);
                        }
                    }
                };

            try
            {
                foreach (var e in input.Entries)
                    register("", e);

                destDirInfo.RootWrapper.compressMultiple(addDic, progress);
            }
            finally
            {
                //Free up the created streams.
                foreach (var s in addDic.Values)
                {
                    s.Dispose();
                }
            }


            return input;
        }

        IEntryList compress(IEntryList input, string param, IWorkProgress progress, string type)
        {
            var stringDic = StringUtils.ParamStringToDic(param);
            if (!stringDic.ContainsKey("dest"))
                throw new ArgumentException("dest not specified.");
            string destPath = stringDic["dest"];
            COFEShared.CurrentUserProfile.Directory.CreateVirtualDirectory(destPath, type);

            return Transfer(input, param, progress);
        }

        IEntryList CompressZip(IEntryList input, string param, IWorkProgress progress)
        {
            return compress(input, param, progress, "zip");
        }

        IEntryList CompressSevenZip(IEntryList input, string param, IWorkProgress progress)
        {
            return compress(input, param, progress, "7z");
        }

        #endregion

        #region Data

        #endregion

        #region Public Properties

        #endregion

    }
}
